#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>


typedef enum pa_t 
{ 
    pa_encrypt = 0, 
    pa_decrypt = 1 
} pa_t;

pa_t pa = pa_encrypt;

#define FILENAME_SZ 1024	
#define KEY_SZ		1024	
#define RDBUF_SZ 	4096


char in_file	[FILENAME_SZ];
char out_file	[FILENAME_SZ];
char key		[KEY_SZ];

int 			ifd = STDIN_FILENO;
int				ofd = STDOUT_FILENO;

int
encrypt()
{
	unsigned int len = 0;
	unsigned int pos = 0;
	unsigned char ibuf[RDBUF_SZ] = { 0 };
	unsigned char obuf[sizeof(ibuf)] = { 0 };

	int kl = strlen(key);
    while( (len = read(ifd, ibuf, sizeof(ibuf))) > 0)
    {
        unsigned int j = 0;
        for (j = 0; j < len; j++, pos %= kl)
		{
	         obuf[j] = ~ibuf[j] + key[pos++];
		}

        if (write(ofd, obuf, len) != len)
	        return 0;
    }

    return 1;
}

int
decrypt()
{
	unsigned int len = 0;
	unsigned int pos = 0;
	unsigned char ibuf[RDBUF_SZ] = { 0 };
	unsigned char obuf[sizeof(ibuf)] = { 0 };

	int kl = strlen(key);
   	while( (len = read(ifd, ibuf, sizeof(ibuf))) > 0 )
   	{
       	unsigned int j = 0;
       	for (j = 0; j < len; j++, pos %= kl)
		{
        	obuf[j] = ~(ibuf[j] - key[pos++]);
		}
		
       	if ( write(ofd, obuf, len) != len )
        	return 0;
   	}
	
	return 1;
}

void
usage(char ** argv)
{
	printf("usage: %s -k <key> [-i <infile>] [-o <outfile>] [-e*|d]\n", argv[0]);
}

void
procopt(int argc, char ** argv)
{
extern char *optarg;

	int c;
   	while ((c = getopt(argc, argv, "dei:k:o:?")) != EOF)
   	{
		switch (c) 
       	{
        	case 'd':
	       	{
				pa = pa_decrypt;
           	}		   
           	break;

        	case 'e':
	       	{
				pa = pa_encrypt;
           	}		   
           	break;

        	case 'i':
	       	{
				if (strlen(optarg) >= FILENAME_SZ) exit(-1);
	        	strcpy(in_file,optarg);
           	}		   
           	break;

        	case 'k':
	       	{
				if (strlen(optarg) >= KEY_SZ) exit(-1);
	        	strcpy(key,optarg);
           	}		   
           	break;

        	case 'o':
	       	{
				if (strlen(optarg) >= FILENAME_SZ) exit(-1);
	        	strcpy(out_file,optarg);
           	}		   
           	break;

        	case '?':
	       	{
				usage(argv);
			    exit(1);
			}
           	break;
       	}
   	}
	
	/* key can not be empty */
	if ( strlen(key) == 0 )
	{
		printf ("key is undefined\n");
		usage(argv);
		exit(1);
	}

	if ( strlen(in_file) > 0 && strlen(out_file))
	{
		if ( strcmp(in_file, out_file) == 0 )
		{
			printf ("infile and outfile are the same\n");
			usage(argv);
			exit(1);
		}
	}
}



int 
main(int argc, char **argv)
{
	procopt(argc, argv);
	
	if ( strlen(in_file) > 0 && (ifd = open(in_file, O_RDONLY)) < 0 )
	{
		perror("Can't open in_file");
		exit(1);
	}

	if ( strlen(out_file) > 0 && (ofd = open(out_file, O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0 )
	{
		perror("Can't open out_file");
		exit(1);
	}

	if (pa == pa_encrypt)
	{
		if ( ! encrypt() )
		{
			printf ("Failed to encrypt\n");
			return (-1);
		}
	}
	else
	if (pa == pa_decrypt)
	{
		if ( ! decrypt() )
		{
			printf ("Failed to decrypt\n");
			return (-1);
		}
	}

	return 0;	
}
